package com.chenfan.mcn.controller;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chenfan.common.exception.BusinessException;
import com.chenfan.mcn.constant.NumberCst;
import com.chenfan.mcn.enums.OperateLockEnum;
import com.chenfan.mcn.enums.StarMonthlySettlementStatusEnum;
import com.chenfan.mcn.exception.McnErrorCode;
import com.chenfan.mcn.extension.Res;
import com.chenfan.mcn.extension.excel.ExcelUtils;
import com.chenfan.common.extension.validation.groups.Add;
import com.chenfan.common.extension.validation.groups.Modify;
import com.chenfan.common.extension.lock.DistributedLockTemplate;
import com.chenfan.mcn.model.common.KVPair;
import com.chenfan.mcn.model.dto.*;
import com.chenfan.mcn.model.vo.*;
import com.chenfan.mcn.service.StarMonthlySettlementService;
import com.chenfan.mcn.utils.BusinessAssert;
import com.chenfan.mcn.utils.CommonUtil;
import com.chenfan.mcn.utils.ValidatorUtil;
import com.chenfan.privilege.common.config.SearchAuthority;
import com.chenfan.privilege.common.config.UrlAuth;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * 红人月度结算单
 *
 * @author wulg
 * @since 2022-01-13
 */
@Slf4j
@RestController
@RequestMapping("/starMonthlySettlement")
public class StarMonthlySettlementController {

    @Autowired
    private StarMonthlySettlementService settlementService;


    /**
     * 分页查询-红人月度结算单
     *
     * @param searchDTO
     * @return
     */
    @PostMapping("/getPage")
    @UrlAuth({"starMonthlySettlement_search"})
    public Res<Page<StarMonthlySettlementListVO>> getPage(@SearchAuthority(filterCustomer = false) StarMonthlySettlementSearchDTO searchDTO) {
        return Res.ok(settlementService.getPage(searchDTO));
    }


    /**
     * 新建-红人月度结算单
     *
     * @param saveDTO
     * @return
     */
    @PostMapping("/add")
    @UrlAuth({"starMonthlySettlement_add"})
    private Res<Long> addSettlement(@Validated({Add.class}) @RequestBody StarMonthlySettlementSaveDTO saveDTO) {
        if (CommonUtil.isEmptyArray(saveDTO.getAttachmentFile())) {
            saveDTO.setAttachmentFile(null);
        }
        return Res.ok(settlementService.addSettlement(saveDTO));
    }

    /**
     * 修改-红人月度结算单
     *
     * @param saveDTO
     * @return
     */
    @PostMapping("/modify")
    private Res<Boolean> modifySettlement(@RequestBody StarMonthlySettlementSaveDTO saveDTO) {
        if (CommonUtil.isEmptyArray(saveDTO.getAttachmentFile())) {
            saveDTO.setAttachmentFile(null);
        }
        if (StarMonthlySettlementStatusEnum.isApproval(saveDTO.getApprovalStatus())) {
            //参数校验后置
            ValidatorUtil.validate(saveDTO, Modify.class);
        } else {
            BusinessAssert.notNull(saveDTO.getId(), McnErrorCode.PARAM_ERROR, "结算单ID不能为空");
        }
        return Res.ok(DistributedLockTemplate.execute(OperateLockEnum.STAR_MONTHLY_SETTLEMENT.generateKey(saveDTO.getId()),
                () -> settlementService.modifySettlement(saveDTO)));
    }

    /**
     * 添加执行单明细-分页查询
     *
     * @param queryDTO
     * @return
     */
    @PostMapping("/getExcutionOrders")
    public Res<Page<StarMonthlySettlementExcutionOrderVO>> getExcutionOrders(@Validated @RequestBody StarMonthlySettlementExcutionOrderQueryDTO queryDTO) {
        return Res.ok(settlementService.getExcutionOrders(queryDTO));
    }

    /**
     * 分页查询结算单明细
     *
     * @param queryDTO
     * @return
     */
    @PostMapping("/getDetailPage")
    public Res<Page<StarMonthlySettlementDetailListVO>> getDetailPage(@Validated @RequestBody StarMonthlySettlementDetailQueryDTO queryDTO) {
        return Res.ok(settlementService.getDetailPage(queryDTO));
    }

    /**
     * （全部）确认-添加结算单明细
     *
     * @param dto
     * @return
     */
    @PostMapping(value = "/addDetails")
    public Res<Boolean> addDetails(@Validated @RequestBody StarMonthlySettlementDetailAddDTO dto) {
        return Res.ok(DistributedLockTemplate.execute(OperateLockEnum.STAR_MONTHLY_SETTLEMENT.generateKey(dto.getSettlementId()),
                () -> settlementService.addDetails(dto)));
    }

    /**
     * 批量添加执行单-导入Excel
     *
     * @param multipartFile
     * @return 导入失败的文件ID
     */
    @Deprecated
    //@PostMapping("/excel/importDetails")
    public Res<String> importDetails(@RequestParam Long id, @RequestParam MultipartFile multipartFile) {
        BusinessAssert.notNull(id, McnErrorCode.PARAM_ERROR, "结算单ID不能为空");
        List<RelatedExcutionOrderInputDTO> inputList = null;
        try {
            inputList = ExcelUtils.importExcel(multipartFile, RelatedExcutionOrderInputDTO.class);
        } catch (Exception e) {
            throw new BusinessException(McnErrorCode.IMPORT_EXCEL_FAILED);
        }
        return Res.ok(settlementService.importDetails(id, inputList));
    }

    /**
     * HR计税批量审批-导入Excel
     *
     * @param multipartFile
     * @return 导入失败的文件ID
     */
    @PostMapping("/excel/importAccruedTax")
    @UrlAuth({"starMonthlySettlement_audit"})
    public Res<String> importAccruedTax(@RequestParam MultipartFile multipartFile) {
        List<AccruedTaxInputDTO> inputList = null;
        try {
            inputList = ExcelUtils.importExcel(multipartFile, AccruedTaxInputDTO.class);
        } catch (Exception e) {
            throw new BusinessException(McnErrorCode.IMPORT_EXCEL_FAILED);
        }
        return Res.ok(settlementService.importAccruedTax(inputList));
    }

    /**
     * 下载失败原因 导入执行单/批量计税&审核
     *
     * @param response
     * @param fileId
     */
    @GetMapping("/excel/download")
    public void excelDownload(HttpServletResponse response, String fileId) {
        log.error("收到红人月度结算单-Excel下载失败文件请求，fileId={}", fileId);
        settlementService.excelDownload(response, fileId);
    }

    /**
     * 修改结算单明细
     *
     * @param dto
     * @return
     */
    @PostMapping(value = "/modifyDetail")
    public Res<Boolean> modifyDetail(@Validated @RequestBody StarMonthlySettlementDetailModifyDTO dto) {
        return Res.ok(DistributedLockTemplate.execute(OperateLockEnum.STAR_MONTHLY_SETTLEMENT.generateKey(dto.getSettlementId()),
                () -> settlementService.modifyDetail(dto)));
    }

    /**
     * （批量）删除结算单明细
     *
     * @param dto
     * @return
     */
    @Deprecated
    @PostMapping(value = "/deleteDetails")
    public Res<Boolean> deleteDetails(@Validated @RequestBody StarMonthlySettlementDetailDeleteDTO dto) {
        return Res.ok(DistributedLockTemplate.execute(OperateLockEnum.STAR_MONTHLY_SETTLEMENT.generateKey(dto.getSettlementId()),
                () -> settlementService.deleteDetails(dto)));
    }

    /**
     * 修改单据状态
     *
     * @param id     {@link Long}
     * @param status {@link Integer}
     * @return {@link Boolean}
     */
    @GetMapping(value = "/changeStatus")
    @UrlAuth({"starMonthlySettlement_submit","starMonthlySettlement_resubmit","starMonthlySettlement_withdraw","starMonthlySettlement_cancel"})
    public Res<Boolean> changeStatus(@RequestParam Long id, @RequestParam Integer status) {
        BusinessAssert.notNull(id, McnErrorCode.PARAM_ERROR, "结算单ID不能为空");
        BusinessAssert.notNull(status, McnErrorCode.PARAM_ERROR, "单据状态不能为空");
        BusinessAssert.notNull(StarMonthlySettlementStatusEnum.anyMatch(status,
                StarMonthlySettlementStatusEnum.APPROVAL, StarMonthlySettlementStatusEnum.CANCEL, StarMonthlySettlementStatusEnum.INVALID),
                McnErrorCode.PARAM_ERROR, "非法操作！");
        return Res.ok(DistributedLockTemplate.execute(OperateLockEnum.STAR_MONTHLY_SETTLEMENT.generateKey(id),
                () -> settlementService.changeStatus(id, status)));
    }

    /**
     * 结算单-打款金额汇总
     *
     * @param id
     * @return
     */
    @GetMapping("/paySummary")
    public Res<StarMonthlySettlementPaySummaryVO> paySummary(@RequestParam Long id) {
        BusinessAssert.notNull(id, McnErrorCode.PARAM_ERROR, "结算单ID不能为空");
        return Res.ok(settlementService.paySummary(id));
    }

    /**
     * 详情页
     *
     * @param id
     * @return
     */
    @GetMapping("/detail")
    public Res<StarMonthlySettlementDetailVO> detail(@RequestParam Long id) {
        BusinessAssert.notNull(id, McnErrorCode.PARAM_ERROR, "结算单ID不能为空");
        return Res.ok(settlementService.detail(id));
    }

    /**
     * 导出- 子结算执行单
     *
     * @param id
     * @param response
     */
    @GetMapping("/exportSub")
    public void exportSub(@RequestParam Long id, HttpServletResponse response) {
        BusinessAssert.notNull(id, McnErrorCode.PARAM_ERROR, "子结算单ID不能为空");
        try {
            KVPair<String, List<StatementPerformListExportVO>> kvPair = settlementService.exportSubSettlement(id);
            String sheetName = kvPair.getK();
            ExcelUtils.exportExcel(kvPair.getV(), sheetName, StatementPerformListExportVO.class, sheetName + "-结算执行单.xlsx", response);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 导出-线下结算的执行单
     *
     * @param response
     */
    @PostMapping("/exportDetail")
    public void exportDetail(@Validated @RequestBody StarMonthlySettlementDetailQueryDTO queryDTO, HttpServletResponse response) {
        try {
            BusinessAssert.notNull(queryDTO.getSettlementForm(), McnErrorCode.PARAM_ERROR, "结算形式不能为空");
            List<StatementPerformListExportVO> dataList = settlementService.exportSettlementDetailList(queryDTO);
            String sheetName = NumberCst.ONE.equals(queryDTO.getSettlementForm()) ? "线下-结算执行单" : "线上-结算执行单";
            ExcelUtils.exportExcel(dataList, sheetName, StatementPerformListExportVO.class, sheetName + ".xlsx", response);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 导出-单个红人月度结算单
     */
    @GetMapping("/exportOneStatement")
    @UrlAuth({"starMonthlySettlement_export_detail"})
    public void exportOneStatement(@RequestParam Long id, HttpServletResponse response) {
        BusinessAssert.notNull(id, McnErrorCode.PARAM_ERROR, "结算单ID不能为空");
        try {
            KVPair<String, List<StarMonthlyOneSettlementExportVO>> kvPair = settlementService.exportOneStatement(id);
            String sheetName = kvPair.getK();
            ExcelUtils.exportExcel(kvPair.getV(), sheetName, StarMonthlyOneSettlementExportVO.class, sheetName + "结算单.xlsx", response);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 导出红人月度结算单列表
     */
    @PostMapping("/exportStatementList")
    @UrlAuth({"starMonthlySettlement_export_list"})
    public void exportStatementList(@SearchAuthority(filterCustomer = false) StarMonthlySettlementSearchDTO searchDTO, HttpServletResponse response) {
        try {
            BusinessAssert.notNull(searchDTO.getExportType(), McnErrorCode.PARAM_ERROR, "请先选择导出类型");
            if (NumberCst.ONE.equals(searchDTO.getExportType())) {
                KVPair<String, List<StatementListExportVO>> kvPair = settlementService.exportStatementList(searchDTO);
                String sheetName = kvPair.getK();
                ExcelUtils.exportExcel(kvPair.getV(), sheetName, StatementListExportVO.class, sheetName + ".xlsx", response);
            } else if (NumberCst.TWO.equals(searchDTO.getExportType())) {
                KVPair<String, List<StatementPerformListExportVO>> kvPair = settlementService.exportStatementPerformList(searchDTO);
                String sheetName = kvPair.getK();
                ExcelUtils.exportExcel(kvPair.getV(), sheetName, StatementPerformListExportVO.class, sheetName + ".xlsx", response);
            } else {
                throw new BusinessException(McnErrorCode.PARAM_ERROR.code(), "请正确标记导出类型");
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 复制作废的结算单
     * @param id
     * @return
     */
    @GetMapping("/copyInvaild")
    public Res<Long> copyInvaild(@RequestParam Long id) {
        BusinessAssert.notNull(id, McnErrorCode.PARAM_ERROR, "作废的结算单ID不能为空");
        return DistributedLockTemplate.execute(OperateLockEnum.STAR_MONTHLY_SETTLEMENT.generateKey(id),
                () -> Res.ok(settlementService.copyInvaild(id)));
    }

    /**
     * 确认付款
     * @param ids 结算单明细ID 数组
     */
    @PostMapping("/confirmPayment")
    @UrlAuth({"starMonthlySettlement_surePayment"})
    public Res<Object> confirmPayment(@RequestBody List<Long> ids) {
        BusinessAssert.notEmpty(ids, McnErrorCode.PARAM_ERROR, "请选择该结算单明细");
        settlementService.confirmPayment(ids);
        return Res.ok();
    }

    /**
     * 取消付款
     * @param ids 结算单明细ID 数组
     */
    @PostMapping("/cancelPayment")
    @UrlAuth({"starMonthlySettlement_cancelPayment"})
    public Res<Object> cancelPayment(@RequestBody List<Long> ids) {
        BusinessAssert.notEmpty(ids, McnErrorCode.PARAM_ERROR, "请选择该结算单明细");
        settlementService.cancelPayment(ids);
        return Res.ok();
    }
}
